package com.gitee.wsl.ext.string

import com.gitee.wsl.ext.base.isIdentifierStart

fun String?.isNotEmpty(): Boolean = !isNullOrEmpty()

fun String?.isNotBlank(): Boolean = !isNullOrBlank()

val String.isSpaceString:Boolean
    get() {
        for(i in indices){
            if (!this[i].isWhitespace()) {
                return false
            }
        }
        return true
    }

private val bareKeyCharSet = (('A'..'Z') + ('a'..'z') + ('0'..'9') + '-' + '_')
    .toCharArray()
    .concatToString()

/**
 * Returns true if the specified key is a bare key, a key containing only
 * alphanumeric, underscore, and hyphen characters.
 *
 * @return `true` if this key is a bare key.
 */
internal fun String.isBareKey() = all { it in bareKeyCharSet }

/**
 * Returns true if the specified key contains at least one unescaped double
 * quote and no single quotes.
 *
 * @return `true` if this key can be written as a literal key.
 */
internal fun String.isLiteralKeyCandidate(): Boolean {
    for ((i, char) in withIndex()) {
        if (char == '\'') {
            return false
        } else if (char == '"' && (i == 0 || this[i - 1] != '\\')) {
            return true
        }
    }

    return false
}

fun String.isContainEmoji(): Boolean {
    val account = this
    val len = account.length
    val isEmoji = false
    for (i in 0 until len) {
        when(val hs = account[i].code){
            in 0xd800 .. 0xdbff ->{
                if (account.length > 1) {
                    val ls = account[i + 1]
                    val uc = (hs - 0xd800) * 0x400 + (ls.code - 0xdc00) + 0x10000
                    if (uc in 0x1d000..0x1f77f) {
                        return true
                    }
                }
            }
            in 0x2100..0x27ff->{
                return hs!= 0x263b
            }
            in 0x2B05 .. 0x2b07-> return true
            in 0x2934 .. 0x2935-> return true
            in 0x3297 .. 0x3299 -> return true
            else->{
                if((hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50 || hs == 0x231a)) return true
            }
        }
        if (!isEmoji && account.length > 1 && i < account.length - 1) {
            val ls = account[i + 1]
            if (ls.code == 0x20e3) {
                return true
            }
        }
    }
    return isEmoji
}

/**
 * 功能：判断一个字符串是否包含特殊字符
 *
 * @return true 提供的参数string不包含特殊字符
 * @return false 提供的参数string包含特殊字符
 */
fun String.isConSpeCharacters(): Boolean {
    return replace("[\u4e00-\u9fa5]*[a-z]*[A-Z]*\\d*-*_*\\s*".toRegex(), "").isEmpty()
}

fun String.isConCharacters(): Boolean {
    return replace("[a-z]*[A-Z]*\\d*-*_*\\s*".toRegex(), "").isEmpty()
}

fun String.Companion.substringEquals(a: String, aIndex: Int, b: String, bIndex: Int, count: Int): Boolean {
    if (count == 0) return true
    if (aIndex < 0 || bIndex < 0) return false
    if (aIndex + count > a.length) return false
    if (bIndex + count > b.length) return false
    for (n in 0 until count) if (a[aIndex + n] != b[bIndex + n]) return false
    return true
}

fun String.isNotQuoted() = !(startsWith("\"") && endsWith("\""))

/**
 * Check if this String has length in [range]
 */
infix fun String.spans(range: IntRange): Boolean = length in range

/**
 * Returns true if all chars in [this] are ASCII symbols
 */
val String.isAscii: Boolean get() = toCharArray().none { it < ' ' || it > '~' }

val String.isIdentifier: Boolean get() {
    if (this.isEmpty()) {
        return false
    } else {
        return this[0].isIdentifierStart && all { it.isIdentifierStart || it in '0'..'9' }
    }
}

val String.isMixedCase: Boolean get() {
    return any { it.isUpperCase() } && any { it.isLowerCase() }
}


// Sometimes keyword can be found in the middle of word.
// This returns information if index points only to the keyword
fun String.isIndependentPhrase(
    code: String,
    index: Int,
): Boolean {
    if (index == code.lastIndex) return true
    if (code.length == this.length) return true

    val charBefore = code[maxOf(index - 1, 0)]
    val charAfter = code[minOf(index + this.length, code.lastIndex)]

    if (index == 0) {
        return charAfter.isDigit().not() && charAfter.isLetter().not()
    }

    return charBefore.isLetter().not() &&
            charAfter.isDigit().not() && (charAfter == code.last() || charAfter.isLetter().not())
}


/**
 * Checks if a String represents a binary number (contains only '0' and '1' characters).
 *
 * @return `true` if the String represents a binary number, `false` otherwise.
 */
fun String.isBinary(): Boolean {
    if (this.isBlank()) {
        return false
    }
    val set = toSet().toMutableSet().apply {
        removeAll(listOf('0', '1'))
    }
    return set.isEmpty()
}

fun String.isNumeric():Boolean = all{it.isDigit()}

fun String.isCharEquals():Boolean = countOccurrences( charAt(0)) == length


/**
 * 是否以指定字符串开头<br></br>
 * 如果给定的字符串和开头字符串都为null则返回true，否则任意一个值为null返回false<br></br>
 * <pre>
 * CharSequenceUtil.startWith("123", "123", false, true);   -- false
 * CharSequenceUtil.startWith("ABCDEF", "abc", true, true); -- true
 * CharSequenceUtil.startWith("abc", "abc", true, true);    -- false
</pre> *
 *
 * @param str          被监测字符串
 * @param prefix       开头字符串
 * @param ignoreCase   是否忽略大小写
 * @param ignoreEquals 是否忽略字符串相等的情况
 * @return 是否以指定字符串开头
 * @since 5.4.3
 */
fun CharSequence.startWith(
    prefix: CharSequence,
    ignoreCase: Boolean,
    ignoreEquals: Boolean
): Boolean {
    val str = this

    val isStartWith = str.toString()
        .regionMatches(0, prefix.toString(), 0, prefix.length, ignoreCase = ignoreCase)

    if (isStartWith) {
        return !ignoreEquals || !str.contentEquals(prefix, ignoreCase)
    }
    return false
}

/**
 * 是否以指定字符串开头，忽略相等字符串的情况
 *
 * @param str    被监测字符串
 * @param prefix 开头字符串
 * @return 是否以指定字符串开头并且两个字符串不相等
 */
fun CharSequence.startWithIgnoreEquals(prefix: CharSequence): Boolean {
    return startWith(prefix, false, true)
}

/**
 * 是否以指定字符串开头，忽略大小写
 *
 * @param str    被监测字符串
 * @param prefix 开头字符串
 * @return 是否以指定字符串开头
 */
fun CharSequence.startWithIgnoreCase(prefix: CharSequence): Boolean {
    return startWith( prefix, true,false)
}